﻿using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using Beaker.OpenCube.Render.Maths;
using Beaker.OpenCube.Render.Materials;

namespace Beaker.OpenCube.Render.Shapes
{
	public sealed class Plane
		: Shape
	{
		private readonly Point3 _point;
		private readonly Vector3 _normal;
		private readonly IMaterial _material;

		/// <summary>
		/// Initializes a new instance of the <see cref="Plane" /> class.
		/// </summary>
		/// <param name="point">The point thru witch the plane passes.</param>
		/// <param name="normal">The normal of the plane.</param>
		/// <param name="material">The surface of the plane.</param>
		public Plane( Point3 point, Vector3 normal, IMaterial material )
		{
			_point = point;
			_normal = normal;
			_material = material;
		}

		/// <summary>
		/// Gets the point thru witch this plane passes.
		/// </summary>
		/// <value>
		/// The point.
		/// </value>
		[Pure]
		public Point3 Point
		{

			get { return _point; }
		}

		/// <summary>
		/// Computes the intersection with the given ray.
		/// </summary>
		/// <param name="scene">The scene.</param>
		/// <param name="ray">The ray to compute an intersection with.</param>
		/// <returns>
		/// An intersection or <c>null</c> when there was no intersection.
		/// </returns>
		[Pure]
		public override Intersection ComputeIntersection( Scene scene, Ray ray )
		{
			// compute intersection
			// plane:				(p-a)∙n=0			(_point - a) * _normal
			// ray:					o+td				(ray.Origin + distance * ray.Direction)
			// substitude point:	(o+td-a)∙n=0		((ray.Origin + distance * ray.Direction) - a) * _normal
			// solve t:				t=(p-a)/(d∙n)		(_point - ray.Origin) * _normal / ( ray.Direction * _normal );
			double distance = ( _point - ray.Origin ) * _normal / ( ray.Direction * _normal );

			// t has distance greater as 0
			if( distance >= Helpers.Precision )
				return new Intersection( scene, this, ray, distance, _normal, _material );

			return null;
		}
	}
}